//-----------------------------------------------------------------------------
// Name: DisplayManager.cpp
// Auth: Bradford J. Smith & Robert Darty
// Desc: contains all of the interface required to manipulate the view as well
//		as any additional state information.
//-----------------------------------------------------------------------------

#include "DisplayManager.h"
#include "Assert.h"
#include "SharedConstants.h"
#include <iostream>
#include <GL/glut.h>
#include <Windows.h>
#include <math.h>
#include "corona/corona.h"
using namespace std;
//-----------------------------------------------------------------------------
// Constants:
//-----------------------------------------------------------------------------
const float kInitialCameraPitch = 0.0f;
const float kInitialCameraHeading = 360.0f;
const float kInitialCameraOffsetOrtho = 2.0f;
const float kInitialCameraOffsetProjection = 20.0f;

const float kMinCameraOffset = 0.5f;

//Calculates the length of a vector
float VectorLength(const tVector3f& vector)
{
	return (float) sqrt((vector[0] * vector[0]) + (vector[1] * vector[1]) + (vector[2] * vector[2]));
}

//-----------------------------------------------------------------------------
// cDisplayManager:
//-----------------------------------------------------------------------------
cDisplayManager::cDisplayManager() :
	mWindowWidth(kInitialWindowWidth),
	mWindowHeight(kInitialWindowHeight),
	mProjectionMode(kPerspective),
	mCameraPitch(kInitialCameraPitch),
	mCameraHeading(kInitialCameraHeading),
	mCameraOffset(kInitialCameraOffsetOrtho)
{
	// Start the camera looking at the origin from down the z-Axis
	mCameraPosition[0] = 0.0f;
	mCameraPosition[1] = 1.9f;
	mCameraPosition[2] = 0.0f;
	mCameraHeading = 0.0f;
	mCameraPitch = 0.0f;
	mStrafeSpeed = 0.0f;
	mForwardSpeed = 0.0f;
}

cDisplayManager::~cDisplayManager()
{

}

//-----------------------------------------------------------------------------
void cDisplayManager::SetWindowArea(int width, int height)
{
	mWindowWidth = width;
	mWindowHeight = height;
}

//-----------------------------------------------------------------------------
// Returns the current Window Width - used to make sure HUD stays constant even when window resizes
int cDisplayManager::GetWindowWidth()
{
	return mWindowWidth;
}

//-----------------------------------------------------------------------------
//Returns window height
int cDisplayManager::GetWindowHeight()
{
	return mWindowHeight;
}

//-----------------------------------------------------------------------------
void cDisplayManager::SetPerspectiveMode()
{
	// update the mode and then force the projection to update
	mProjectionMode = kPerspective;
	UpdateProjection();
}

//-----------------------------------------------------------------------------
void cDisplayManager::SetOrthographicMode()
{
	// update the mode and then force the projection to update
	mProjectionMode = kOrthographic;
	UpdateProjection();
}

//-----------------------------------------------------------------------------
void cDisplayManager::UpdateProjection()
{
	// we are going to modify the projection matrix
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	switch (mProjectionMode)
	{
	case kOrthographic:
		{
			// create a basic orthographic projection
			glOrtho(-500.0, 500.0, -500.0, 500.0, -500.0, 500.0);

			// reset the camera offset to be a reasonable distance.
			mCameraOffset = kInitialCameraOffsetOrtho;
		}
		break;
	case kPerspective:
		{
			// compute the aspect ratio, being careful to cast to float
			float aspectRatio = (float)mWindowWidth / (float)mWindowHeight;

			// create a basic perspective projection
			gluPerspective(90.0f, aspectRatio, 0.2f, 100.0f);

			// reset the camera offset to be a reasonable distance.
			mCameraOffset = kInitialCameraOffsetProjection;
		}
		break;
	default:
		{
			// Unsupported mode!
			Assert(false);
		}
		break;
	}

	// reset the model view in any case.
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

//-----------------------------------------------------------------------------
void cDisplayManager::SetCameraPitch(float pitch)
{
	//Sets the Camera's pitch
	mCameraPitch = pitch;
}

//-----------------------------------------------------------------------------
void cDisplayManager::SetCameraHeading(float heading)
{
	//Sets the Camera Heading
	mCameraHeading = heading;
}

//-----------------------------------------------------------------------------
void cDisplayManager::SetCameraOffset(float offset)
{
	//Sets camera offset
	if (offset < kMinCameraOffset)
	{
		mCameraOffset = kMinCameraOffset;
	}
	else 
	{
		mCameraOffset = offset;
	}
}

//-----------------------------------------------------------------------------
float cDisplayManager::GetCameraPitch()
{
	//Gets pitch
	return mCameraPitch;
}

//-----------------------------------------------------------------------------
float cDisplayManager::GetCameraHeading()
{
	//Gets heading
	return mCameraHeading;
}

float cDisplayManager::GetCameraOffset()
{
	//Gets Offset
	return mCameraOffset;
}

//-----------------------------------------------------------------------------
void cDisplayManager::UpdateCamera(float elapsedTimeS)
{
	// Test the assumptions of the camera
	Assert(mCameraOffset >= kMinCameraOffset);
	// reset the model view matrix to identity
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	// get tuning values
	float kMaxVelocity = 5.0f; // meters per second
	float kMaxRotationVelocity = 2.0f; // radians per second
	float kRotationFriction = 0.4f; // percentage loss per second [0.0f - 1.0]
	float kMovementFriction = 0.2f; // percentage loss per second [0.0f - 1.0]

	// update rotation
	//Keeps a 0-360 degree scale
	if (mCameraHeading < 0.0f)
	{
		mCameraHeading = mCameraHeading+360.0f;
	}
	else if (mCameraHeading > 360.0f)
	{
		mCameraHeading = mCameraHeading - 360.0f;
	}


	// The F Directions are the directions relative to forward movement (converts to radians)
	float xFDirection = cos((mCameraHeading-90)*kDegreesToRadians);
	float zFDirection = cos((mCameraHeading)*kDegreesToRadians);
	// The S Directions are directions relative to strafing movement (converts to radians)
	float xSDirection = sin((mCameraHeading-90)*kDegreesToRadians);
	float zSDirection = sin((mCameraHeading)*kDegreesToRadians);


	// Change the position of the camera based on Forward/Strafe speed & time elapsed
	mCameraPosition[0] += ((mForwardSpeed * elapsedTimeS) * xFDirection)+((mStrafeSpeed * elapsedTimeS) * xSDirection);
	mCameraPosition[2] += ((mForwardSpeed * elapsedTimeS) * zFDirection)+((mStrafeSpeed * elapsedTimeS) * zSDirection);

	// cap pitch to avoid being directly above or below the center of interest.
	if (mCameraPitch > 69.9f)
	{
		mCameraPitch = 69.9f;
	}
	else if (mCameraPitch < -69.9f)
	{
		mCameraPitch = -69.9f;
	}

	// convert to radians
	float pitch = mCameraPitch * kDegreesToRadians;
	float heading = mCameraHeading * kDegreesToRadians;

	// convert the polar coordinates of heading and pitch to a direction
	tVector3f direction = {sin(heading) * cos(pitch), sin(pitch), cos(heading) * cos(pitch)};
	
	// construct the actual camera COI using Position, direction and offset
	// the direction, and the offset.
	tVector3f cameraCOI;
	for (int i = 0; i < 3; ++i)
	{
		cameraCOI[i] = mCameraPosition[i] + (direction[i] * mCameraOffset);
	}

	// move the camera using the lookat data
	gluLookAt(mCameraPosition[0], mCameraPosition[1], mCameraPosition[2], 
			  cameraCOI[0], cameraCOI[1], cameraCOI[2], 
			  kUpVector[0], kUpVector[1], kUpVector[2]);
}

void cDisplayManager::Move(eMoveMode mode)
{
	// adjust acceleration applied to the ship based on input
	switch (mode)
	{
	case kStartMoveRight:
		//Because strafing is bi-directional - right is represented 
		//by positive numbers, left by negative
		mStrafeSpeed = 15.0f;
		break;
	case kStartMoveLeft:
		mStrafeSpeed = -15.0f;
		break;
	case kStopMoveRight:
		mStrafeSpeed=0.0f;
		break;
	case kStopMoveLeft:
		mStrafeSpeed=0.0f;
		break;
	case kStartMoveForward:
		//Forwards represented by positive numbers
		mForwardSpeed = 15.0f;
		break;
	case kStartMoveBackward:
		//negative by backwards
		mForwardSpeed = -15.0f;
		break;
	case kStopMoveForward:
		mForwardSpeed = 0.0f;
		break;
	case kStopMoveBackward:
		mForwardSpeed = 0.0f;
		break;
	default:
		// unsupported mode!
		Assert(false);
		break;
	}
}

//-----------------------------------------------------------------------------
void cDisplayManager::DrawHUD()
{
	//Ensure Projection mode is selected
	glMatrixMode(GL_PROJECTION);
	//Push the matrix
	glPushMatrix();
	glLoadIdentity();
	//Set up 2d Ortho disply
	gluOrtho2D(0, mWindowWidth, 0, mWindowHeight);
	glScalef(1, -1, 1);
	glTranslatef(0, (float)-mWindowHeight, 0);

	//Switch to Model view
	glMatrixMode(GL_MODELVIEW);

	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	//Set the color for HUD output
	glColor3f(1.0f, 0.0f, 0.0f);
	//Protype for outputing health - will need changing once it's implemented
	string strHealth("Health: ");
	glRasterPos2i(20, 20);
	int len = (int)strHealth.size();
	for (int i = 0; i < len; i++)
	{
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, strHealth[i]);
	}
	//Set semi-transparent crosshair - again a prototype
	glColor4f(1.0f, 0.0f, 0.0f, 0.25f);
	glBegin(GL_QUADS);
		glVertex2i((mWindowWidth/2)-2,(mWindowHeight/2)+15);
		glVertex2i((mWindowWidth/2)+2,(mWindowHeight/2)+15);
		glVertex2i((mWindowWidth/2)+2,(mWindowHeight/2)-15);	
		glVertex2i((mWindowWidth/2)-2,(mWindowHeight/2)-15);

		glVertex2i((mWindowWidth/2)-15, (mWindowHeight/2)+2);
		glVertex2i((mWindowWidth/2)+15, (mWindowHeight/2)+2);
		glVertex2i((mWindowWidth/2)+15, (mWindowHeight/2)-2);
		glVertex2i((mWindowWidth/2)-15, (mWindowHeight/2)-2);

	glEnd();
	
	//Prototype bullet display 
	glRasterPos2i(mWindowWidth-70, mWindowHeight-30 );
	corona::Image* BulletIconData = corona::OpenImage("./textures/bullet_icon.tga", corona::PF_R8G8B8A8);
	BulletIconData = corona::FlipImage(BulletIconData, corona::CA_X);
	if(BulletIconData)
	{
		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);		
			glDrawPixels(BulletIconData->getWidth(), BulletIconData->getHeight(), 
			GL_RGBA, GL_UNSIGNED_BYTE, BulletIconData->getPixels());
		glRasterPos2i(mWindowWidth-70, mWindowHeight-50 );
			glPixelStorei(GL_UNPACK_ALIGNMENT, 1);		
			glDrawPixels(BulletIconData->getWidth(), BulletIconData->getHeight(), 
			GL_RGBA, GL_UNSIGNED_BYTE, BulletIconData->getPixels());
	}
	//Set back to Projection matrix and pop
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	//Set model view
	glMatrixMode(GL_MODELVIEW);
}

